Decorator: decorator is fastest way to wrap a function. this can also give us power to add extra functionality to wrapping function. we can decorator
def decor(funcPointer): # Decorator definition
def internalFunc(*args,**kwds):
print("before")
i = funcPointer(*args,**kwds)
print("after")
return i
return internalFunc
@decor #wrapping is done by using "@" symbol
def sample(a,b,c):
print(a,b,c)
#here sample is wrapped into decorator and will be a internal function of the decorator function as in above example.
#all the arguments passed will be packed in *args and **kwds. and passed to internal function and mapped to the "sample" function arguments
sample(1,2,3) #calling the function
#output
before
1 2 3
after
#here 1 2 3 will be packed into *args in decor function and will be unpacked in the internal function resulting with a = 1, b = 2, c = 3. this will work as any normal function call.
# we can add extra code before and after calling the "funcPointer". which gives us additional functionality.
Example: if we want to have a timer for the sample function. we can start the timer before calling "funcPointer" and end it after. similarly, we do logging also.
sample(b = 2,c=3,a =1) #calling the function
#output
before
1 2 3
after
#here all args will captured by the **kwds and unpacked to internal function which results in positional arguments for internal function. assignment will be done like a = 1, b = 2, c = 3
def mydecorator(func): # takes in a function pointer
def innerfunc(*args, **kwds):
import time
start = time.time()
i = func(*args, **kwds) # this is remembered
end = time.time()
print(b-a, " seconds")
return i # this matches with the return of actual function sent in.
return innerfunc # this return statement is for mydecorator function.
# so far, with above code, what we have accomplished is that, wrapped the function that was passed.
def normalFunc():
print("normalFunc is called")
print(normalFunc) # this is shown as , just a function
def dynamicFunc(): # going to create a dynamic decorator - note, '@mydecorator' just before this function definition
print('dynamicFunc is called')
# below statement is how to dynamically apply a decorator in live session
decoratedDynamicFunc = mydecorator(dynamicFunc) # here , we will have decorated version of the function in decoratedDynamicFunc variable and non decorated version in dynamicFunc - you could verify by printing the mentioned variables
print(dynamicFunc) # this is shown as , just a function
print(decoratedDynamicFunc) # this is shown as a function that is internal to mydecorator
@mydecorator # this is equivalent to decoratedFunc = mydecorator(decoratedFunc)
def decoratedFunc():
print('decoratedFunc is called')
print(decoratedFunc) # this is shown as a function that is internal to mydecorator - Not sure, if there is a way to get the actual function from the decorated version of the function
Example: Cascaded decorator
def decor1(funcPointer): # 1st Decorator definition
def internalFunc(*args,**kwds):
print("before 1st decorator")
i = funcPointer(*args,**kwds)
print("after 1st decorator")
return i
return internalFunc
def decor2(funcPointer): # 2nd Decorator definition
def internalFunc(*args,**kwds):
print("before 2nd decorator")
i = funcPointer(*args,**kwds)
print("after 2nd decorator")
return i
return internalFunc
@decor1 #decorating two times
@decor2
def sample(a,b,c):
print(a,b,c)
sample(1,2,3) # this will give output as below by following the call order
#OUTPUT:
#before 1st decorator
#before 2nd decorator
#1 2 3
#after 2nd decorator
#after 1st decorator
Example: Time log of a function using Decorator
def timeLogDecorator(func): # takes in a function pointer
def innerfunc(*args, **kwds):
import time
start = time.time() # start time
i = func(*args, **kwds) # this is remembered
end = time.time() # end time
file = open(r"d:/temp/log.txt", "a") # opening file
data = "Time taken by the '" + func.__name__ + "' is " + str(round(end-start, 4)) + " seconds\n" # data
file.write(data) # write data
file.close() # close
return i # this matches with the return of actual function sent in.
return innerfunc # this return statement is for mydecorator function.
# so far, with above code, what we have accomplished is that, wrapped the function that was passed and taking the time log of it.
@timeLogDecorator
def normalFunc():
print("normalFunc is called")
normalFunc() # this will log the time taken by the normal func in given file location
# this will give write as "Time taken by the 'normalFunc' is 0.004 seconds" in log file